package com.university.mentalhealthplatform.global.redisGuard;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.TimeUnit;

@Service
//缓存击穿通过互斥锁或逻辑过期的方式解决,逻辑过期实现:
// 缓存对象中增加一个逻辑过期时间,当缓存对象过期时,判断逻辑过期时间是否小于当前时间,
// 如果小于当前时间,则认为缓存对象过期,否则认为缓存对象逻辑过期,并重新设置缓存对象
public class CacheService {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private DatabaseService databaseService;

    public <T> T getCacheData(String key, Class<T> type) {
        Object value = redisTemplate.opsForValue().get(key);
        if (value == null) {
            return null;
        }

        // 检查是否为空对象
        if ("".equals(value.toString())) {
            return null;
        }

        // 检查是否逻辑过期
        CacheObject cacheObject = (CacheObject) value;
        if (cacheObject.isLogicalExpire()) {
            return null;
        }
        //缓存穿透处理
        //缓存穿透可以通过缓存空对象、布隆过滤器等方式解决。
        //
        //缓存空对象：
        //在getCacheData方法中，当数据库查询结果为空时，我们缓存一个空对象并设置一个短暂的过期时间
        // 在getCacheData方法中添加以下代码来缓存空对象
        if (value == null) {
            // 查询数据库
            T dbData = getDbData(key, type);
            if (dbData == null) {
                // 缓存空对象
                CacheObject<Object> emptyCacheObject = new CacheObject<>();
                emptyCacheObject.setData("");
                redisTemplate.opsForValue().set(key, emptyCacheObject, 60, TimeUnit.SECONDS);
                return null;
            }
            // 缓存数据库查询结果
            setCacheData(key, dbData, 60, TimeUnit.SECONDS);
            return dbData;
        }


        return type.cast(cacheObject.getData());
    }



    public <T> void setCacheData(String key, T data, long timeout, TimeUnit timeUnit) {
        CacheObject<T> cacheObject = new CacheObject<>();
        cacheObject.setData(data);
        cacheObject.setExpireTime(System.currentTimeMillis() + timeUnit.toMillis(timeout));
        redisTemplate.opsForValue().set(key, cacheObject, timeout, timeUnit);
        //缓存雪崩可以通过设置不同的过期时间、使用缓存更新订阅等方式解决。
        //设置不同的过期时间：
        //为每个缓存项设置一个随机的过期时间范围。
        // 在setCacheData方法中添加以下代码来设置随机过期时间
        Random random = new Random();
        int randomTimeout = (int) (timeout + random.nextInt(1000)); // 随机增加0-1000毫秒
        redisTemplate.opsForValue().set(key, cacheObject, randomTimeout, timeUnit);
    }
    public <T> T getDbData(String key, Class<T> type) {
        return databaseService.getDataFromDatabase(key, type);
    }

    private static class CacheObject<T> {
        private T data;
        private long expireTime;

        public T getData() {
            return data;
        }

        public void setData(T data) {
            this.data = data;
        }

        public long getExpireTime() {
            return expireTime;
        }

        public void setExpireTime(long expireTime) {
            this.expireTime = expireTime;
        }

        public boolean isLogicalExpire() {
            return System.currentTimeMillis() > expireTime;
        }
    }
}

